from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import mysql.connector
import time
from selenium.common.exceptions import TimeoutException, StaleElementReferenceException

# MySQL 配置
db_config = {
    'user': 'root',
    'password': 'Wlj98192188?',  # 替换为你的MySQL密码
    'host': 'localhost',
    'database': 'mooc_data'
}

# 初始化数据库连接
def init_db():
    conn = mysql.connector.connect(**db_config)
    cursor = conn.cursor()
    # 创建表格
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS courses (
            id INT AUTO_INCREMENT PRIMARY KEY,
            course_name VARCHAR(255),
            school_name VARCHAR(255),
            teacher VARCHAR(255),
            team_members TEXT,
            participants INT,
            progress VARCHAR(50),
            description TEXT
        )
    """)
    conn.commit()
    return conn, cursor

# 插入数据到数据库
def insert_data(cursor, conn, data):
    sql = """
    INSERT INTO courses (
        course_name, school_name, teacher, team_members, 
        participants, progress, description
    ) VALUES (%s, %s, %s, %s, %s, %s, %s)
    """
    try:
        # 将团队成员列表转换为字符串
        data = list(data)
        if isinstance(data[3], list):  # 检查 team_members 是否为列表
            data[3] = ', '.join(data[3])  # 使用逗号将成员名拼接成字符串

        # 插入数据
        cursor.execute(sql, tuple(data))
        conn.commit()
        print("成功插入一条数据")
    except Exception as e:
        conn.rollback()
        print(f"插入数据失败: {e}")

# 配置 Selenium
options = webdriver.ChromeOptions()
options.add_argument('--headless')  # 无头模式
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 40)
driver.set_page_load_timeout(120)  # 设置页面加载超时时间
options.add_argument('--disable-gpu')  # 禁用 GPU 加速
options.add_argument('--no-sandbox')  # 禁用沙盒模式
options.add_argument('--disable-dev-shm-usage')  # 禁用共享内存
options.add_argument('--disable-extensions')  # 禁用扩展

# 模拟登录
def login():
  url="https://www.icourse163.org"
  driver.get(url)

  login = driver.find_element(By.CLASS_NAME, "_3uWA6")  # 查找登录按钮
  login.click()  # 点击登录按钮

  log=driver.find_element(By.TAG_NAME,"iframe")
  driver.switch_to.frame(log)  # 切换到登录框

  WebDriverWait(driver, 15, 0.5).until(
      EC.presence_of_element_located((By.XPATH, "//*[@id='login-form']/div")))  # 等待登录框加载

  username = driver.find_element(By.XPATH, "//input[@id='phoneipt']")
  username.send_keys("18120958838")  # 输入用户名
  time.sleep(2)

  password = driver.find_element(By.XPATH, "//input[@type='password' and @name='email' and contains(@class, 'j-inputtext')]")
  password.send_keys("wlj98192188")  # 输入密码
  time.sleep(2)

  button = driver.find_element(By.XPATH, "//*[@id='submitBtn']")
  button.click()  # 点击登录按钮
  time.sleep(2)

  driver.switch_to.default_content()
  time.sleep(5)
  driver.maximize_window()
  time.sleep(3)
  print("登录成功，准备爬取课程信息...")
# 爬取课程列表并进入每个课程的详情页
def scrape_courses():
    # 初始化数据库
    conn, cursor = init_db()
    print("开始！")
    course_data_list = []

    scroll_pause_time = 1  # 每次滚动后的等待时间
    max_scroll_attempts = 50  # 最大滚动次数，防止无限循环
    scroll_attempts = 0

    while scroll_attempts < max_scroll_attempts:
        try:
            # 检查目标元素是否加载到页面
            target_element = driver.find_element(By.XPATH, "//div[@class='_15K5J']")
            print("已经定位到目标元素")

            # 滚动到目标元素
            driver.execute_script("arguments[0].scrollIntoView(true);", target_element)
            print("已经滚动到目标元素")
            time.sleep(2)  # 等待加载
            break
        except Exception:
            # 如果目标元素尚未加载，则继续滚动
            driver.execute_script("window.scrollBy(0, 800);")  # 每次向下滚动 800 像素
            time.sleep(scroll_pause_time)

            # 增加滚动尝试次数
            scroll_attempts += 1
            print(f"滚动尝试次数：{scroll_attempts}")

            # 如果滚动尝试达到最大次数，退出并报告
            if scroll_attempts >= max_scroll_attempts:
                print("已滚动多次仍未找到目标元素，退出")
                return course_data_list

    # 找到课程卡片
    course_cards = wait.until(EC.presence_of_all_elements_located((By.XPATH, "//div[@class='_2mbYw commonCourseCardItem']")))
    print("已经找到多张课程卡片")

    for i, card in enumerate(course_cards):
        try:
            # 将课程滚动到可视区域并点击
            driver.execute_script("arguments[0].scrollIntoView();", card)
            time.sleep(2)
            card.click()

            # 等待新窗口/标签页打开
            WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(2))
            driver.switch_to.window(driver.window_handles[-1])
            print("已切换到课程详情页。")
            # 点击课程卡片进入详情
            time.sleep(10)  # 等待页面加载

            # 解析课程详情数据
            course_data = scrape_course_details(driver)
            if course_data:
                # 插入数据到数据库
                insert_data(cursor, conn, (
                    course_data['course_name'],
                    course_data['school_name'],
                    course_data['teacher'],
                    course_data['team_members'],
                    course_data['participants'],
                    course_data['progress'],
                    course_data['description']
                ))
            print("已成功存储一条数据！")
                # 关闭详情页并返回主页面
            driver.close()
            driver.switch_to.window(driver.window_handles[0])
            print("已返回主课程页面。")

            time.sleep(5)  # 等待页面加载

            # 重新获取课程列表
            course_cards = wait.until(EC.presence_of_all_elements_located((By.XPATH, "//div[@class='_2mbYw commonCourseCardItem']")))

        except StaleElementReferenceException:
            print("捕获到 StaleElementReferenceException，重新加载课程列表")
            if len(driver.window_handles) > 1:
                driver.close()
                driver.switch_to.window(driver.window_handles[0])

    return course_data_list


# 解析并爬取课程详情数据
def scrape_course_details(driver):
    try:
        # 确保页面完全加载
        WebDriverWait(driver, 30).until(
            lambda d: d.execute_script('return document.readyState') == 'complete'
        )
        print("页面完全加载完成")

        # 等待课程详情部分加载
        WebDriverWait(driver, 30).until(
            EC.visibility_of_element_located((By.XPATH, "//div[@id='g-body']"))
        )
        print("课程详情加载完成")

        # 1. 获取课程名
        try:
            course_name_element = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located(
                    (By.XPATH, "//span[@class='course-title f-ib f-vam']")
            ))
            course_name = course_name_element.text
        except TimeoutException:
            course_name = "未知课程"
        print(f"课程名: {course_name}")

        # 2. 获取学校名
        try:
            school_name = driver.find_element(By.XPATH,"//div[@class='m-teachers']//a[@class='ga-click m-teachers_school-img f-ib']/img").get_attribute("alt")
        except Exception:
            school_name = "未知学校"
        print(f"学校名: {school_name}")

        # 3. 获取参与人数
        try:
            participants_element = driver.find_element(By.XPATH, "//span[@class='count']")
            participants_text = participants_element.text if participants_element else "0"
            participants = int("".join(filter(str.isdigit, participants_text)))
        except Exception:
            participants = 0
        print(f"参与人数: {participants}")

        # 4. 获取课程进度
        try:
            progress_element = driver.find_element(By.XPATH, "//span[@class='text']")
            progress = progress_element.text if progress_element else "未知进度"
        except Exception:
            progress = "未知进度"
        print(f"课程进度: {progress}")

        # 5. 获取课程简介
        try:
            description_element = driver.find_element(By.XPATH, "//div[@class='course-heading-intro_intro']")
            description = description_element.text if description_element else "暂无简介"
        except Exception:
            description = "暂无简介"
        print(f"课程简介: {description}")

        # 滚动查找目标元素
        scroll_attempts = 0
        max_scroll_attempts = 10  # 最大滚动次数
        scroll_pause_time = 2  # 每次滚动后的等待时间

        while scroll_attempts < max_scroll_attempts:
            try:
                # 尝试查找目标元素
                target_element = driver.find_element(By.XPATH, "//div[@class='m-teachers_teacher-list']")
                print("找到教师信息元素！")

                # 滚动到目标元素
                driver.execute_script("arguments[0].scrollIntoView(true);", target_element)
                print("已经滚动到教师信息位置")
                time.sleep(scroll_pause_time)  # 等待页面加载
                break  # 找到目标元素后退出循环
            except Exception:
                # 如果未找到目标元素，则向下滚动页面
                driver.execute_script("window.scrollBy(0, 800);")  # 每次向下滚动 800 像素
                print(f"未找到目标元素，尝试向下滚动... 第 {scroll_attempts + 1} 次")
                time.sleep(scroll_pause_time)  # 等待页面内容加载
                scroll_attempts += 1  # 增加滚动次数

                # 如果达到最大滚动次数
                if scroll_attempts >= max_scroll_attempts:
                    print("滚动达到最大次数，未找到目标元素")

        # 6. 获取教师名
        try:
            teacher = driver.find_element(By.XPATH, "//div[@class='m-teachers_teacher-list']//h3[@class='f-fc3']").text
        except Exception:
            teacher = "未知教师"
        print(f"教师名: {teacher}")

        # 7. 获取团队成员
        try:
            # 获取团队成员的元素列表
            team_member_elements = driver.find_elements(By.XPATH,"//div[@class='m-teachers_teacher-list']//h3[@class='f-fc3']")

            # 提取每个团队成员的名字
            team_members = [member.text for member in team_member_elements]

            # 如果列表为空，则没有找到团队成员
            if not team_members:
                team_members = "暂无团队成员"
        except Exception:
            team_members = "暂无团队成员"

        print(f"团队成员: {team_members}")

        # 返回课程数据
        course_data = {
            "course_name": course_name,
            "school_name": school_name,
            "teacher": teacher,
            "team_members": team_members,
            "participants": participants,
            "progress": progress,
            "description": description,
        }
        print("课程数据:", course_data)
        return course_data

    except TimeoutException as e:
        print(f"加载课程详情失败: 页面加载超时，异常信息: {str(e)}")
        return None

    except Exception as e:
        print(f"加载课程详情时发生未知错误: {str(e)}")
        return None


# 主函数
def main():
    # 初始化数据库
    conn, cursor = init_db()
    login()  # 登录

    try:
        # 抓取课程信息并插入数据库
        print("开始爬取课程信息...")
        scrape_courses()
        print("数据存储成功！")

    except Exception as e:
        print("爬取失败：", e)

    finally:
        cursor.close()
        conn.close()
        driver.quit()

if __name__ == "__main__":
    main()

